﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.IO;
using System.Reflection;

namespace Core.LockStep
{
    public class LockStepSystem : EntitySystem, IAwake, IStart, IUpdate
    {
        public int perFrameTime = 40;
        // 每一个消息头添加了 8个BYTE（长度+玩家ID）
        public readonly int frameDataHeadCount = 8;

        public int currentSumTime;
        public readonly int maxSumTime = 1000;

        public float remain;

        // 帧延迟的半程时间
        public int netDelayTime;

        public ILockStepHelper lockStepHelper;
        public ISystemEnumerable<IFrameUpdateSystem> frameSystems;
        public ISystemEnumerable<IInterpolationUpdateSystem> interpolationSystems;
        public ISystemEnumerable<IStepDataSender> stepDataSenders;
        public ISystemEnumerable<IResetSystem> resetSystems;

        // 战斗信息数据
        public LockstepInfoData lockstepInfoData;

        // 服务器权威帧数据
        public int serverFrameIndex;
        private Dictionary<int, FrameDataStream> serverFrames = new Dictionary<int, FrameDataStream>();
        private Dictionary<int, DateTime> sendTimes = new Dictionary<int, DateTime>();

        public readonly int maxSendOffset = 15;
        public int clientFrameIndex;
        public int PredictFrameIndex => clientFrameIndex + predictUpdateCount;
        private Dictionary<int, FrameDataStream> clientFrames = new Dictionary<int, FrameDataStream>();

        private PlayState playState;
        private bool isPlay;

        // 客户端预测帧数，数值大于延迟帧数操作会流畅，但是需要回滚的量会大，性能下降
        private readonly int predictUpdateCount = 0;
        // 是否记录服务器数据做回放
        private bool isRecord;
        // 是否本地单人模式（不发送网络命令）
        private bool isLocalMode;

        private byte[] intBytes = new byte[4];

        private readonly int maxMissCount = 10;
        private readonly int maxCheckCount = 5;
        private readonly int checkMul = 20;
        private int currentMissCount;
        private int checkMissCount;
        private int checkReceiveCount;

        public void Awake()
        {
            lockStepHelper = GetHelper<ILockStepHelper>();

            Subscribe<SetBattleInfoData>(SetBattleInfoData);
            Subscribe<GetBattleInfoData>(GetBattleInfoData);
            Subscribe<SendStepData>(SendStepData);
            Subscribe<ReceiveFrameData>(ReceiveFrameData);
            Subscribe<ReceiveFrameTime>(ReceiveFrameTime);
            Subscribe<StartLockStep>(StartLockStep);
            Subscribe<PauseLockStep>(PauseLockStep);
            Subscribe<ResumeLockStep>(ResumeLockStep);
            Subscribe<SaveLockStepData>(SaveLockStepData);
            Subscribe<LoadLockStepData>(LoadLockStepData);
            Subscribe<StopLockStep>(StopLockStep);
            Subscribe<GetLockStepRunningInfo>(GetLockStepRunningInfo);
            Subscribe<AnalysisGameAssembly>(AnalysisGameAssembly);
        }

        public void Start()
        {
            frameSystems = GetCustomSystems<IFrameUpdateSystem>();
            stepDataSenders = GetCustomSystems<IStepDataSender>();
            interpolationSystems = GetCustomSystems<IInterpolationUpdateSystem>();
            resetSystems = GetCustomSystems<IResetSystem>();
        }

        private void Reset()
        {
            lockstepInfoData = null;
            isRecord = false;
            isLocalMode = false;

            currentSumTime = 0;
            remain = 0;
            netDelayTime = 0;

            currentMissCount = 0;
            checkMissCount = 0;
            checkReceiveCount = 0;

            serverFrameIndex = 0;
            foreach (var item in serverFrames.Values)
                PoolTools.Push(item);
            serverFrames.Clear();

            sendTimes.Clear();

            clientFrameIndex = 0;
            foreach (var item in clientFrames.Values)
                PoolTools.Push(item);
            clientFrames.Clear();

            playState = PlayState.None;
            isPlay = false;

            lockStepHelper.Reset();

            ResetSystems();
        }

        private void ResetSystems()
        {
            foreach (var item in resetSystems)
            {
                try
                {
                    item.Reset();
                }
                catch (Exception ex)
                {
                    Log.Error(ex);
                }
            }
        }

        private void AnalysisGameAssembly(ref AnalysisGameAssembly data)
        {
            lockStepHelper.AnalysisGameAssembly(data.gameAssembly);
        }

        private void GetBattleInfoData(ref GetBattleInfoData data)
        {
            data.lockstepInfoData = lockstepInfoData;
        }

        private void SetBattleInfoData(ref SetBattleInfoData data)
        {
            lockstepInfoData = data.lockstepInfoData;
        }

        private void LoadLockStepData(ref LoadLockStepData data)
        {
            if (!File.Exists(data.path))
            {
                Log.Error($"未找到数据路径:{data.path}");
                return;
            }

            Reset();

            // 加载数据
            LoadSaveBytes(data.path);

            // 播放游戏录像
            playState = PlayState.Record;

            Log.Info("录像播放开始");

            LockStepStart();
        }

        private void LoadSaveBytes(string path)
        {
            //TODO 可以优化为需要这一帧数据时再加载，而不是一开始全部加载

            using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                // 读取战斗信息
                ReadBattleInfoBytes(fileStream);

                while (fileStream.Position < fileStream.Length)
                {
                    // 长度
                    var length = ReadIntFormStream(fileStream);

                    // 空帧
                    if (length == 0)
                        continue;

                    // 帧索引
                    var frameIndex = ReadIntFormStream(fileStream);

                    SetServerFrameIndex(frameIndex);

                    // 数据
                    var frameData = PoolTools.Pop<FrameDataStream>();
                    frameData.SetBuffer(fileStream, length);

                    if (serverFrames.ContainsKey(frameIndex))
                        Log.Info("已存在相同索引帧数据，覆盖");

                    serverFrames[frameIndex] = frameData;
                }
            }
        }

        private void ReadBattleInfoBytes(FileStream fileStream)
        {
            // 长度
            var length = ReadIntFormStream(fileStream);
            if (length == 0)
                return;

            // 数据
            var frameData = PoolTools.Pop<FrameDataStream>();
            frameData.SetBuffer(fileStream, length);

            lockstepInfoData = lockStepHelper.CreateLockstepInfoData();
            lockstepInfoData.ReadFrom(frameData);

            PoolTools.Push(frameData);
        }

        private int ReadIntFormStream(FileStream fileStream)
        {
            fileStream.Read(intBytes, 0, 4);
            return BitConverter.ToInt32(intBytes);
        }

        private void SaveLockStepData(ref SaveLockStepData data)
        {
            switch (data.saveType)
            {
                case LockStep.SaveLockStepData.SaveType.Bytes:
                    SaveLockStepDataBytes(data.path);
                    break;
                case LockStep.SaveLockStepData.SaveType.String:
                    SaveLockStepDataString(data.path);
                    break;
            }
        }

        private void SaveLockStepDataBytes(string path)
        {
            CheckFilePath(path);

            using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                // 写入战斗信息
                WriteBattleInfoBytes(fileStream);

                var frameIndex = 0;

                while (frameIndex < serverFrameIndex)
                {
                    if (serverFrames.TryGetValue(frameIndex, out var frameData))
                    {
                        // 长度
                        WriteIntToStream(fileStream, frameData.Length);
                        // 帧索引
                        WriteIntToStream(fileStream, frameIndex);
                        // 数据
                        fileStream.Write(frameData.GetBuffer(), 0, frameData.Length);
                    }
                    frameIndex++;
                }
            }

            Log.Info($"二进制数据保存完成:{path}");
        }

        private void WriteBattleInfoBytes(FileStream fileStream)
        {
            if (lockstepInfoData == null)
            {
                WriteIntToStream(fileStream, 0);
                return;
            }

            var frameData = PoolTools.Pop<FrameDataStream>();
            lockstepInfoData.WriteTo(frameData);

            // 战斗信息长度
            WriteIntToStream(fileStream, frameData.Length);
            // 战斗信息数据
            fileStream.Write(frameData.GetBuffer(), 0, frameData.Length);

            PoolTools.Push(frameData);
        }

        private void SaveLockStepDataString(string path)
        {
            CheckFilePath(path);

            var builder = new StringBuilder();

            // 写入战斗信息
            if (lockstepInfoData != null)
                builder.AppendLine(lockstepInfoData.GetDescription());

            var frameIndex = 0;

            while (frameIndex < serverFrameIndex)
            {
                if (serverFrames.TryGetValue(frameIndex, out var frameData))
                {
                    // 帧索引
                    builder.AppendLine($"frameIndex:{frameIndex}");

                    // 数据
                    frameData.SeekBegin();

                    int playerDataLength = 0;
                    int playerId = 0;

                    while (frameData.Position < frameData.Length)
                    {
                        if (frameData.Position >= playerDataLength)
                        {
                            // 一个玩家的输入长度
                            playerDataLength += frameData.ReadInt32();
                            // 玩家Id
                            playerId = frameData.ReadInt32();

                            if (frameData.Position >= playerDataLength)
                                continue;
                        }

                        var stepData = lockStepHelper.GetStepDataFromStream(frameData);
                        stepData.PlayerId = playerId;

                        // 添加到当前帧
                        builder.AppendLine(stepData.GetDescription());
                    }

                    builder.AppendLine();
                }

                frameIndex++;
            }

            File.WriteAllText(path, builder.ToString());

            Log.Info($"文本数据保存完成:{path}");
        }

        private void WriteIntToStream(FileStream fileStream, int value)
        {
            fileStream.WriteByte((byte)value);
            fileStream.WriteByte((byte)(value >> 8));
            fileStream.WriteByte((byte)(value >> 16));
            fileStream.WriteByte((byte)(value >> 24));
        }

        private void CheckFilePath(string path)
        {
            var info = new FileInfo(path);
            if (File.Exists(info.FullName))
                File.Delete(info.FullName);

            if (!Directory.Exists(info.DirectoryName))
                Directory.CreateDirectory(info.DirectoryName);
        }

        private void StartLockStep(ref StartLockStep data)
        {
            Reset();

            isRecord = data.isRecord;
            isLocalMode = data.isLocalMode;

            lockstepInfoData = data.lockstepInfo;

            netDelayTime = data.netDelayTime;
            currentSumTime = netDelayTime;

            playState = PlayState.RealTime;

            Log.Info("实时控制开始");

            LockStepStart();
        }

        private void PauseLockStep(ref PauseLockStep data)
        {
            isPlay = false;
        }

        private void ResumeLockStep(ref ResumeLockStep data)
        {
            isPlay = true;
        }

        private void StopLockStep(ref StopLockStep data)
        {
            Log.Info("实时控制结束");

            LockStepEnd();
        }

        private void GetLockStepRunningInfo(ref GetLockStepRunningInfo data)
        {
            data.netDelayTime = netDelayTime;
            data.clientFrameIndex = clientFrameIndex;
            data.serverFrameIndex = serverFrameIndex;
        }

        private void ReceiveFrameTime(ref ReceiveFrameTime data)
        {
            if (!isPlay)
                return;

            UpdateNetDelayTime(data.frameIndex, data.serverFrameIndex, data.currentSumTime);
        }

        private void ReceiveFrameData(ref ReceiveFrameData data)
        {
            if (!isPlay)
                return;

            SetServerFrameIndex(data.frameIndex);

            SetServerFrameData(data.datas, data.frameIndex);

            // 判断是否需要回滚
            CheckRollBack(data.frameIndex, data.datas);

            TryRemoveServerFrame(data.frameIndex, true);
            TryRemoveClientFrame(data.frameIndex);
        }

        private void UpdateNetDelayTime(int frameIndex, int remoteFrameIndex, int remoteSumTime)
        {
            var old = remoteSumTime;

            if (sendTimes.TryGetValue(frameIndex, out var time))
            {
                sendTimes.Remove(frameIndex);
                var span = (int)(DateTime.Now - time).TotalMilliseconds;
                netDelayTime = span > perFrameTime / 2 ? span : span / 2;

                // 同步时间
                var frameOffset = clientFrameIndex - remoteFrameIndex;

                if (Mathf.Abs(frameOffset) > 0)
                {
                    currentSumTime = frameOffset > 0 ? 0 : perFrameTime;
                }
                else
                {
                    remoteSumTime += span / 2;
                    currentSumTime = (int)(currentSumTime + remoteSumTime) / 2;

                    // 解析消息卡顿预留时间
                    if (remoteSumTime < perFrameTime * 0.75f &&
                        ((currentSumTime + span) > perFrameTime / 2))
                        currentSumTime /= 2;
                }
            }
        }

        private string GetTime()
        {
            var time = DateTime.Now;
            return $"time(s:{time.Second},m:{time.Millisecond})";
        }

        private void TryRemoveClientFrame(int frameIndex)
        {
            if (clientFrames.TryGetValue(frameIndex, out var clientFrameData))
            {
                // 移除本地帧
                clientFrames.Remove(frameIndex);
                PoolTools.Push(clientFrameData);
            }
        }

        // 读取，并执行过了的数据就可以删除了
        // 1. 服务器收到数据，和客户端预测匹配，可以删除
        // 2. 客户端执行到了，不需要isCheck，可以直接删
        private void TryRemoveServerFrame(int frameIndex, bool isCheck)
        {
            if (isRecord)
                return;

            if (isCheck && clientFrameIndex <= frameIndex)
                return;

            if (serverFrames.TryGetValue(frameIndex, out var serverFrameData))
            {
                // 移除权威帧
                serverFrames.Remove(frameIndex);
                PoolTools.Push(serverFrameData);
            }
        }

        private void CheckRollBack(int frameIndex, List<byte[]> datas)
        {
            // 客户端超前服务器才需要判断回滚
            if (PredictFrameIndex > frameIndex)
            {
                serverFrames.TryGetValue(frameIndex, out var serverFrameData);
                clientFrames.TryGetValue(frameIndex, out var clientFrameData);

                if (!IsFrameDataEqual(serverFrameData, clientFrameData, datas))
                    RollBack(frameIndex);
            }
            else
            {
                // 判断操作是否丢失，累计丢失计数
                clientFrames.TryGetValue(frameIndex, out var clientFrameData);
                IsSelfEqual(clientFrameData, datas);
            }

            lockStepHelper.RemoveRollbackData(frameIndex);
        }

        private bool IsFrameDataEqual(FrameDataStream serverFrameData, FrameDataStream clientFrameData, List<byte[]> datas)
        {
            if (!IsSelfEqual(clientFrameData, datas))
                return false;

            // 判断是否是其他玩家输入是否是空帧
            // 本地默认使用空帧预测
            if (!IsOtherPlayerEmpty(serverFrameData))
                return false;

            return true;
        }

        private void MarkMiss(bool isMiss)
        {
            if (isMiss)
            {
                checkReceiveCount = 0;
                checkMissCount++;
                if (checkMissCount > maxCheckCount)
                {
                    checkMissCount = 0;
                    currentMissCount++;
                    if (currentMissCount > maxMissCount)
                        currentMissCount = maxMissCount;
                }
            }
            else
            {
                checkReceiveCount++;
                if (checkReceiveCount > (maxCheckCount * checkMul))
                {
                    checkMissCount = maxCheckCount;
                    checkReceiveCount = 0;
                    currentMissCount--;
                    if (currentMissCount < 0)
                        currentMissCount = 0;
                }
            }
        }

        private bool IsSelfEqual(FrameDataStream clientFrameData, List<byte[]> datas)
        {
            // 本地没有输入，就不会发送，那就是一样的
            if (clientFrameData == null)
                return true;

            if (datas != null)
            {
                var clientBuffer = clientFrameData.GetBuffer();

                // 服务有数据判断
                foreach (var item in datas)
                {
                    if (item.Length >= frameDataHeadCount)
                    {
                        // 第2个int放玩家ID
                        var playerId = BitConverter.ToInt32(item, 4);

                        if (playerId == lockstepInfoData.playerId)
                        {
                            if (clientFrameData.Length != item.Length)
                                return false;

                            for (int i = frameDataHeadCount; i < item.Length; i++)
                            {
                                if (item[i] != clientBuffer[i])
                                {
                                    MarkMiss(true);
                                    return false;
                                }
                            }

                            MarkMiss(false);
                            return true;
                        }
                    }
                }
            }

            MarkMiss(true);
            // 如果没有自己的输入下发，但是本地有，如果本地不是空操作，则需要回滚
            return clientFrameData.Length <= frameDataHeadCount;
        }

        private bool IsOtherPlayerEmpty(FrameDataStream serverFrameData)
        {
            if (serverFrameData == null)
                return true;

            serverFrameData.SeekBegin();
            while (serverFrameData.Position < serverFrameData.Length)
            {
                var startPos = serverFrameData.Position;

                // 一个玩家的输入长度
                var playerDataLength = serverFrameData.ReadInt32();

                var playerId = serverFrameData.ReadInt32();

                if (playerId != lockstepInfoData.playerId)
                {
                    if (playerDataLength != frameDataHeadCount)
                        return false;
                }

                serverFrameData.Seek(startPos + playerDataLength);
            }

            return true;
        }

        private void RollBack(int frameIndex)
        {
            lockStepHelper.Rollback(frameIndex);
            PoolTools.RecycleAllReferences();

            // 回滚时，已经有这一帧的服务器数据，删除客户端帧
            TryRemoveClientFrame(frameIndex);

            // 使用服务器的数据更新到当前帧

            var rollbackIndex = PredictFrameIndex;
            clientFrameIndex = frameIndex - predictUpdateCount;

            while (PredictFrameIndex < rollbackIndex)
            {
                try
                {
                    HandleFrame(perFrameTime);
                }
                catch (Exception ex)
                {
                    Log.Error(ex);
                    return;
                }
            }
        }

        private void SetServerFrameData(List<byte[]> datas, int frameIndex)
        {
            // 空帧
            if (datas == null)
                return;

            if (serverFrames.TryGetValue(frameIndex, out var frameData))
            {
                frameData.Clear();
            }
            else
            {
                frameData = PoolTools.Pop<FrameDataStream>();
                serverFrames[frameIndex] = frameData;
            }

            foreach (var data in datas)
                frameData.WriteBuffer(data);
        }

        private void SetServerFrameIndex(int frameDataIndex)
        {
            serverFrameIndex = frameDataIndex;
        }

        public void SendStepData(ref SendStepData data)
        {
            // 只有自己的数据才通过这个接口传入
            lockStepHelper.AddNeedSendStepData(data.stepData);
        }

        public void LockStepStart()
        {
            isPlay = true;
            new OnLockStepBegin().Publish();
        }

        public void LockStepEnd()
        {
            isPlay = false;
            new OnLockStepEnd().Publish();
        }

        public void Update()
        {
            if (!isPlay)
                return;

            var ms = Time.deltaTime * 1000;

            ms += remain;

            // 剩余的小数部分
            remain = ms % 1;

            var deltaMs = (int)ms;
            currentSumTime += deltaMs;

            currentSumTime = Mathf.Clamp(currentSumTime, -maxSumTime, maxSumTime);

            if (currentSumTime < 0)
                return;

            if (currentSumTime >= perFrameTime)
            {
                lockStepHelper.Interpolation = 1;
                lockStepHelper.SetAllBackupDataInterpolationEnd();
                // 在迭代结束时更新一次帧
                UpdateInterpolationSystems();

                currentSumTime -= perFrameTime;

                EnterFrame(perFrameTime);
            }
            else
            {
                lockStepHelper.Interpolation = Mathf.Min(1, currentSumTime * 1f / perFrameTime);
                // 更新差值帧
                UpdateInterpolationSystems();
            }
        }

        public void EnterFrame(int deltaTime)
        {
            if (CheckIsStopRecord())
                return;

            // 收集数据并发送
            TryCollectFrameDataAndSend();

            HandleFrame(deltaTime);
        }

        private void HandleFrame(int deltaTime)
        {
            // 解析数据并赋值
            TryReadFrameData();

            TryRemoveServerFrame(clientFrameIndex, false);

            // 更新逻辑帧
            UpdateFrameSystems(deltaTime);

            clientFrameIndex++;
        }

        private void UpdateInterpolationSystems()
        {
            var deltaTime = Time.deltaTime;
            foreach (var item in interpolationSystems)
            {
                try
                {
                    item.InterpolationUpdate(deltaTime);
                }
                catch (Exception ex)
                {
                    Log.Error(ex);
                }
            }
        }

        // 检测停止录像模式
        private bool CheckIsStopRecord()
        {
            if (playState != PlayState.Record)
                return false;

            if (PredictFrameIndex < serverFrameIndex)
                return false;

            Log.Info("录像播放结束");

            LockStepEnd();

            return true;
        }

        public void TryReadFrameData()
        {
            try
            {
                // 清理数据
                lockStepHelper.ClearServerParseData();

                ReadFrameData();
            }
            catch (Exception e)
            {
                Log.Error($"解析数据失败，清空当前帧数据。{e}");

                lockStepHelper.ClearServerParseData();
            }
        }

        public void ReadFrameData()
        {
            // 先读取权威帧
            var readIndex = PredictFrameIndex;
            if (!serverFrames.TryGetValue(readIndex, out var frameData) || frameData == null)
            {
                // 没有的话用本地帧预测
                if (!clientFrames.TryGetValue(readIndex, out frameData) || frameData == null)
                {
                    // 空帧跳过
                    return;
                }
            }

            int playerDataLength = 0;
            int playerId = 0;

            frameData.SeekBegin();

            while (frameData.Position < frameData.Length)
            {
                if (frameData.Position >= playerDataLength)
                {
                    // 一个玩家的输入长度
                    playerDataLength += frameData.ReadInt32();
                    // 玩家Id
                    playerId = frameData.ReadInt32();

                    if (frameData.Position >= playerDataLength)
                        continue;
                }

                var stepData = lockStepHelper.GetStepDataFromStream(frameData);
                stepData.PlayerId = playerId;

                // 添加到当前帧
                lockStepHelper.AddServerParseStepData(stepData);
            }
        }

        public void UpdateFrameSystems(int deltaTime)
        {
            // 更新前先备份
            TryBackup();

            foreach (var item in frameSystems)
            {
                try
                {
                    item.FrameUpdate(deltaTime);
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }
            }
        }

        private void TryBackup()
        {
            if (playState == PlayState.Record)
                return;

            if (isLocalMode)
                return;

            lockStepHelper.Backup(PredictFrameIndex);
        }

        public void TryCollectFrameDataAndSend()
        {
            // 录像模式不需要收集数据
            if (playState == PlayState.Record)
                return;

            CollectFrameDataAndSend();
        }

        public void CollectFrameDataAndSend()
        {
            var sendIndex = GetNeedSendFrameIndex();
            if (sendIndex <= serverFrameIndex)
            {
                // 发送nul获取时间
                SendInputStream(null, sendIndex);
                return;
            }

            var frameOffset = sendIndex - PredictFrameIndex;
            // 超过最大提前发送帧不收集输入
            if (frameOffset > maxSendOffset)
            {
                SendInputStream(null, sendIndex);
                return;
            }

            foreach (var item in stepDataSenders)
                item.SendStepData();

            var data = lockStepHelper.GetNeedSendFrameData(sendIndex, lockstepInfoData.playerId);

            if (isLocalMode)
            {
                // 本地模式不发送，直接添加到权威缓存
                serverFrameIndex = sendIndex;
                serverFrames[sendIndex] = data;
            }
            else
            {
                // 添加到本地
                clientFrames[sendIndex] = data;
                SendInputStream(data, sendIndex);
            }
        }

        private void SendInputStream(FrameDataStream data, int frameIndex)
        {
            if (!sendTimes.ContainsKey(frameIndex))
                sendTimes[frameIndex] = DateTime.Now;
            new SendFrameData(data, frameIndex).Publish();
        }

        public int GetNeedSendFrameIndex()
        {
            var delayFrameCount = clientFrameIndex - serverFrameIndex;
            var frameDelayTime = delayFrameCount * perFrameTime;

            // 检测的延迟时间选择 网络延迟与帧延迟的最大值
            var checkDelayTime = Mathf.Max(frameDelayTime, netDelayTime);

            var sendIndex = clientFrameIndex;

            // 如果延迟在提前帧时间内，可以直接发送当前帧，否则需要计算延迟发送帧
            if (checkDelayTime <= predictUpdateCount * perFrameTime)
            {
                sendIndex = PredictFrameIndex;
            }
            else
            {
                // 发送帧 = 当前客户端帧 + 向上取整 ( 延迟时间 / 每帧时间 )
                sendIndex = clientFrameIndex + Mathf.CeilToInt(checkDelayTime * 1f / perFrameTime);
            }

            // 累计丢失帧
            sendIndex += currentMissCount;

            if (isLocalMode)
                sendIndex = PredictFrameIndex;

            return sendIndex;
        }

        public enum PlayState
        {
            None,
            RealTime,
            Record,
        }
    }

    public struct StartLockStep : IEventData
    {
        public LockstepInfoData lockstepInfo;
        public int netDelayTime;
        public bool isRecord;
        public bool isLocalMode;
        public StartLockStep(LockstepInfoData lockstepInfo, int netDelayTime, bool isRecord, bool isLocalMode)
        {
            this.lockstepInfo = lockstepInfo;
            this.netDelayTime = netDelayTime;
            this.isRecord = isRecord;
            this.isLocalMode = isLocalMode;
        }
    }

    public struct PauseLockStep : IEventData { }

    public struct ResumeLockStep : IEventData { }

    public struct StopLockStep : IEventData { }

    public struct SetBattleInfoData : IEventData
    {
        public LockstepInfoData lockstepInfoData;

        public SetBattleInfoData(LockstepInfoData lockstepInfoData)
        {
            this.lockstepInfoData = lockstepInfoData;
        }
    }

    public struct GetBattleInfoData : IEventData
    {
        public LockstepInfoData lockstepInfoData;
    }

    public struct SendStepData : IEventData
    {
        public IStepData stepData;
        public SendStepData(IStepData stepData)
        {
            this.stepData = stepData;
        }
    }

    public struct ReceiveFrameData : IEventData
    {
        public List<byte[]> datas;
        public int frameIndex;
        public ReceiveFrameData(List<byte[]> buffer, int frameIndex)
        {
            this.datas = buffer;
            this.frameIndex = frameIndex;
        }
    }

    public struct ReceiveFrameTime : IEventData
    {
        public int frameIndex;
        public int serverFrameIndex;
        public int currentSumTime;

        public ReceiveFrameTime(int frameIndex, int serverFrameIndex, int currentSumTime)
        {
            this.frameIndex = frameIndex;
            this.serverFrameIndex = serverFrameIndex;
            this.currentSumTime = currentSumTime;
        }
    }

    public struct SendFrameData : IEventData
    {
        public FrameDataStream frameDataStream;
        public int frameIndex;
        public SendFrameData(FrameDataStream frameDataStream, int frameIndex)
        {
            this.frameDataStream = frameDataStream;
            this.frameIndex = frameIndex;
        }
    }

    public struct SaveLockStepData : IEventData
    {
        public enum SaveType
        {
            Bytes,
            String,
        }

        public string path;
        public SaveType saveType;

        public SaveLockStepData(string path, SaveType saveType)
        {
            this.path = path;
            this.saveType = saveType;
        }
    }

    public struct LoadLockStepData : IEventData
    {
        public string path;

        public LoadLockStepData(string path)
        {
            this.path = path;
        }
    }

    public struct OnLockStepBegin : IEventData { }
    public struct OnLockStepEnd : IEventData { }

    public struct GetLockStepRunningInfo : IEventData
    {
        public int netDelayTime;
        public int clientFrameIndex;
        public int serverFrameIndex;
    }

    public struct AnalysisGameAssembly : IEventData
    {
        public Assembly gameAssembly;
        public AnalysisGameAssembly(Assembly gameAssembly)
        {
            this.gameAssembly = gameAssembly;
        }
    }
}